#!/usr/bin/env python3

from random import shuffle, randint
from multiprocessing import Process, Value, Pipe
from time import time, sleep
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s: %(message)s'
)

class MonopolyLikelihood(object):
    """Calcul probability on each Monopoly square (force method)"""
  
    def __init__(self, strategygame, timer, child_conn,ind_machine):
        self.likelihood = [0 for i in range(40)]
        self.strategygame = strategygame
        self.timer = timer
        self.ind_machine = ind_machine
        self.child_conn = child_conn
    
    def __build_response(self):
        """return response for projeteuler"""
        res = ""
        for i in range(3):
            val = max(self.likelihood)
            ind = (self.likelihood.index(val))
            res += str(ind).rjust(2,"0")
            self.likelihood[ind] = -1
            logging.debug("machine {} : response finded : {}"\
                .format(self.ind_machine,res))
            return res
    
    def start(self):
        res = []
        while not self.timer.istimeout():
            self.likelihood[self.strategygame.roll()] +=1
        self.child_conn.send(self.__build_response())
        self.child_conn.close()
      
      
      
class MonopolySimulator(object):
    """
    Comporte toute la strategie de deplacement sur le plateau de jeu Monopoly
    """
 
    N, CC, CH, G2J = range(4)
  
    def __init__(self, dice):
        self.double = 0
        self.dice = dice
        self.pos = 0
        self.board = self.__build_board()
        self.cccards = self.__build_cccards()
        self.chcards = self.__build_chcards()
        shuffle(self.cccards)
        shuffle(self.chcards)
        logging.debug("board " + str(self.board))
        logging.debug("cccards " + str(self.cccards))
        logging.debug("chcards" + str(self.chcards))
    
    def roll(self):
        """TODO"""
        pass
    
    def __go_to_jail(self):
        self.pos = 20
            
    def __build_board(self):
        table = [self.N for i in range(40)]
        table[2] = table[17] = table[33] = self.CC
        table[7] = table[22] = table[37] = self.CH
        table[30] = self.G2J
        return table
    
    def __build_cccards(self):
        table = [None for i in range(14)]
        table.append(self.__idnumber(0))
        table.append(self.__idnumber(10))
        return table
    
    def __build_chcards(self):
        table = [None for i in range(6)]
        table.append(self.__idnumber(0))
        table.append(self.__idnumber(10))
        table.append(self.__idnumber(11))
        table.append(self.__idnumber(24))
        table.append(self.__idnumber(39))
        table.append(self.__idnumber(5))
        table.append(self.__nextrailway)
        table.append(self.__nextrailway)
        table.append(self.__nextutility)
        return table
    
    @staticmethod
    def __idnumber(nb):
        return lambda: nb
  
    @staticmethod
    def __nearthan(nb,nblist):
        res = None
        actualgap = None
        for i in nblist:
            gap = i - nb
            if gap > 0 and (actualgap == None or gap<actualgap):
                res = i
                actualgap = gap
        if res:
            return res
        else:
            return min(nblist)
          
    def __nextrailway(self):
        return self.__nearthan(self.pos,(5,15,25,35))
    
    def __nextutility(self):
        return self.__nearthan(self.pos,(12,18))

class Timer(object):
    def __init__(self, time):
        self.time = time
        self.timeout = Value("b", False)
    
    def istimeout(self):
        return self.timeout.value
  
    def start(self):
        process = Process(target = self.__wait)
        process.start()
  
    def __wait(self):
        logging.debug("Timer started")
        self.timeout.value = False
        sleep(self.time)
        self.timeout.value = True
        logging.debug("Timer ended")
    

class MonopolyLikelihoodValidator(object):
    def __init__(self,nbprocesses = 4, dice = 6, time = 30, minvalidator=0):
        self.nbprocesses = nbprocesses
        self.dice = dice
        self.timer = Timer(time)
        self.minvalidator = minvalidator
    
    def start(self):
        processes = []
        results = []
        average = 0
        nbtentative = 0
        while True:
            nbtentative += 1
            logging.info("Lancement de la simulation {}".format(nbtentative))
            self.timer.start()
            for nb_simulator in range(self.nbprocesses):
                parent_conn, child_conn = Pipe()
                results.append(parent_conn)
                monopolysimulator = MonopolySimulator(self.dice)
                monopolylikelihood = MonopolyLikelihood(
                    monopolysimulator,
                    self.timer,
                    child_conn,
                    nb_simulator
                )
            process = Process(target = monopolylikelihood.play)
            processes.append(process)
            process.start()
            for process in processes:
                process.join()

            res, average = calculateaverage(arrayresponses)
            if average > self.minvalidator:
                logging.info("Simulation {} OK".format(nbtentative))
                logging.info("Reponse : {}".format(res))
                return res
            else:
                logging.info("Simulation {} KO".format(nbtentative))
                logging.info("lower Average : {} with {}".format(res))
    
def calculateaverage(arrayvalues):
    dic = {}
    for value in arrayvalues:
        if value in arrayvalues.keys():
            dic[value] += 1
        else:
            dic[value] = 1
    res = max(dic, key = lambda x: dic[x])
    average = dic[res]/len(arrayvalues)
    return (res, average)
    
"""
Print answer and process time
"""
if __name__=='__main__':
    time_begin = time()
  
    monopolylikelihoodvalidator = MonopolyLikelihoodValidator(
        nbprocesses = 20,
        time = 5,
        minvalidator = 0.9
    )
    euler84 = monopolylikelihoodvalidator.start()
  
    logging.info("Answer : {}".format(euler84))
    logging.info("Time : {}\"".format(time()-time_begin))
